package com.xuanyue.db.xuan.core.index;

import java.io.File;
import java.io.IOException;
import java.util.List;

import com.xuanyue.db.xuan.core.exception.IndexException;
import com.xuanyue.db.xuan.core.exception.SQLException;
import com.xuanyue.db.xuan.core.table.IBitIndex;
import com.xuanyue.db.xuan.core.table.IColumn;
import com.xuanyue.db.xuan.core.table.INumberColumn;
import com.xuanyue.db.xuan.core.table.ISortElement;
import com.xuanyue.db.xuan.core.table.expr.Expr;
import com.xuanyue.db.xuan.core.table.expr.ExprValue;
import com.xuanyue.db.xuan.core.table.expr.ExprValueType;
import com.xuanyue.db.xuan.core.table.expr.SortTemplate;
import com.xuanyue.db.xuan.core.tools.Savor;
import com.xuanyue.db.xuan.msg.VLAUETYPE;

/**
 * 浮点型
 * 真实值 = ((float)持久化值) /eNum
 * 例如    eNum设置为100,  
 *    一个数 12.2  的  持久化值为    (long)(12.2 * 100)  即 1220
 *    
 * 浮点型数据列的存储本质上是一个有符号整形，
 * eNum 表示数据位移，需要是10的N次方。
 * 
 * @author 解观海
 * @email  guanhaixie@sina.cn
 * @date 2020年6月23日
 * @version 0.1
 */
public class FLOATIndex implements INumberColumn<Float>{

	private NumberIndex data;
	
	private long eNum=10000;
	private String path;
	
	
	@Override
	public boolean modify(IColumn newOne,boolean cpDataFromOld) {
		FLOATIndex fi = (FLOATIndex)newOne;
		if(!cpDataFromOld) {
			data.modify(fi.data,false);
			this.eNum=fi.eNum;
			data.getMask().setAll(false);
			return true;
		}else {
			File cfcFile = new File(String.format("%s/ColumnValueFileCache__", path));
			ColumnValueFileCache cfc = new ColumnValueFileCache();
			cfc.init(cfcFile);
			try {
				cfc.open();
				BitIndexIterator ite = new BitIndexIterator(data.getMask());
				ExprValue ev = new ExprValue();
				ev.setType(ExprValueType.FLOAT);
				int id;
				float v;
				int count = 0;
				while(ite.hasNext()) {
					id = ite.next();
					v=this.get(id);
					cfc.write(id);
					cfc.write(v);
					count++;
				}
				this.data.modify(fi.data,false);
				this.eNum=fi.eNum;
				Savor.write(eNum, String.format("%s/eNum",path));
				cfc.seek(0);
				for(int i=0;i<count;i++) {
					id = cfc.readInt();
					v=cfc.readFloat();
					ev.setValue(v);
					this.set(id, ev);
				}
				return true;
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				try {
					cfc.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return false;
	}
	
	private static int powE(int eNum) {
		int r =1;
		for(int i=0;i<eNum;i++) {
			r*=10;
		}
		return r;
	}
	
	@Override
	public void setPath(String path) {
		this.path = path;
		data.setPath(String.format("%s/data", path));
	}
	public FLOATIndex(int size,int eNum) {
		data = new NumberIndex(size);
		this.eNum=powE(eNum);
	}
	public FLOATIndex(int size,int eNum,long zero) {
		data = new NumberIndex(size,zero);
		this.eNum=powE(eNum);
	}
	public void flush(int maxId) {
		data.flush(maxId);
	}
	@Override
	public void equeals(IBitIndex cache, Number valueO) {
		if(valueO==null) {
			cache.copyFrom(data.getMask());
			cache.not();
			return;
		}
		Float value = valueO.floatValue();
		value*=eNum;
		data.equeals(cache, value.longValue());
	}
	@Override
	public void greater(IBitIndex cache, IBitIndex now, Number valueO) {
		Float value = valueO.floatValue();
		value*=eNum;
		data.greater(cache, now, value.longValue());
	}
	@Override
	public void less(IBitIndex cache, IBitIndex now, Number valueO) {
		Float value = valueO.floatValue();
		value*=eNum;
		data.less(cache, now, value.longValue());
	}
	@Override
	public void greaterAndEq(IBitIndex cache, IBitIndex now, Number valueO) {
		Float value = valueO.floatValue();
		value*=eNum;
		data.greaterAndEq(cache, now, value.longValue());
	}
	@Override
	public void lessAndEq(IBitIndex cache, IBitIndex now, Number valueO) {
		Float value = valueO.floatValue();
		value*=eNum;
		data.lessAndEq(cache, now, value.longValue());
	}
	@Override
	public void set(int rowId,ExprValue valueO) {
		
		if(ExprValueType.NULL.is(valueO.getType())) {
			data.set(rowId, valueO);
			return;
		}else if(ExprValueType.FLOAT.is(valueO.getType())) {
			Float value = valueO.getValue();
			value*=eNum;
			ExprValue in = new ExprValue();
			in.setType(ExprValueType.LONG);
			in.setValue(value.longValue());
			data.set(rowId, in);
		}else {
			throw new SQLException("value type ERROR:"+valueO.getType()+"  is not a float value");
		}
	}
	@Override
	public Float get(int rowId) {
		Long v = data.get(rowId);
		if(v==null) {
			return null;
		}
		return v.floatValue()/eNum;
	}
	@Override
	public ExprValue getExprValue(int rowId) {
		return new ExprValue(ExprValueType.FLOAT, get(rowId));
	}
	
	@Override
	public void save() {
		data.save();
		Savor.write(eNum, String.format("%s/eNum",path));
	}

	@Override
	public void load() {
		File p = new File(path);
		if(!p.exists()) {
			throw new IndexException(String.format("%s is not exists", path));
		}
		data.load();
		eNum = Savor.read(String.format("%s/eNum",path));
	}
	@Override
	public void init() {
		File p = new File(path);
		if(!p.exists()) {
			p.mkdirs();
		}
		data.init();
		Savor.write(eNum, String.format("%s/eNum",path));
	}
	
	@Override
	public int getDataSize() {
		return data.getDataSize();
	}
	@Override
	public List<ISortElement> getSortE(SortTemplate sortTemplate) {
		return data.getSortE(sortTemplate);
	}
	@Override
	public boolean checkSortE(SortTemplate sortTemplate) {
		return sortTemplate.isType(SortTemplate.GENERAL);
	}
	@Override
	public VLAUETYPE getType() {
		return VLAUETYPE.FLOAT;
	}
	@Override
	public void saveRow( int rowId){
		data.saveRow(rowId);
	}
	@Override
	public boolean checkType(ExprValue v) {
		return ExprValueType.INT.is(v.getType())||ExprValueType.FLOAT.is(v.getType())||ExprValueType.NULL.is(v.getType());
	}
	@Override
	public void exe(Expr expr, List<IBitIndex> caches) {
		ExprValue value = expr.getValue();
		ExprValue ev = new ExprValue();
		if(ExprValueType.FLOAT.is(expr.getValue().getType())){
			float tar = value.getValue();
			ev.setType(ExprValueType.LONG);
			tar*=eNum;
			ev.setValue((long)tar);
		}else if(ExprValueType.INT.is(expr.getValue().getType())){
			float tar = (int)value.getValue();
			ev.setType(ExprValueType.LONG);
			tar*=eNum;
			ev.setValue((long)tar);
		}else if(ExprValueType.NULL.is(expr.getValue().getType())){
			ev.setType(value.getType());
		}else {
			throw new SQLException("the expr value type is not supoort :"+ value.getType());
		}
		Expr in = new Expr();
		in.setValue(ev);
		in.setOp(expr.getOp());
		data.exe(in, caches);
	}
	@Override
	public int checkAndContSource(Expr expr) {
		ExprValue value = expr.getValue();
		ExprValue ev = new ExprValue();
		if(ExprValueType.FLOAT.is(expr.getValue().getType())){
			float tar = value.getValue();
			ev.setType(ExprValueType.LONG);
			tar *=eNum;
			ev.setValue((long)tar);
		}else if(ExprValueType.NULL.is(expr.getValue().getType())){
			ev.setType(value.getType());
		}else {
			return 0;
		}
		Expr in = new Expr();
		in.setValue(ev);
		in.setOp(expr.getOp());
		return data.checkAndContSource(in);
	}
}
